﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.Exchange.WebServices.Autodiscover;
using Microsoft.Exchange.WebServices.Data;
using Thor.Domain;

namespace Thor.Exchange.Interop
{
    public abstract class ExchangeCalendarProviderBase : CalendarProviderBase
    {
        /// <summary>
        /// Authenticates the specified credentials against the calendar provider.
        /// </summary>
        /// <param name="credentials"></param>
        /// <param name="serviceUrl">Url to the service providing calendar data.</param>
        /// <returns></returns>
        public override AuthenticationResult Authenticate(NetworkCredential credential, string serviceUrl)
        {
            //To authenticate we're currently just trying to make a call to Exchange using
            //the provider credentials. If autentication fails on Exchange an Exception is
            //raised which we trap and include in the result.

            //We should probably revisit this at some point as there must be a better
            //way to do it.

            AuthenticationResult result = new AuthenticationResult();

            try
            {
                var svc = GetService(credential, serviceUrl);

                var resolutionResult = svc.ResolveName(credential.UserName);

                result.Success = true;
                result.Message = "Authenticated.";
            }
            catch (Exception ex)
            {
                result.Success = false;
                result.Message = ex.Message;
            }

            return result;
        }

        /// <summary>
        /// Gets data for a specific calendar.
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        protected override Calendar OnGetCalendar(CalendarRequest request)
        {
            var svc = GetService(request.Credential, request.ServiceUrl);
            
            CalendarFolder calendarFolder = CalendarFolder.Bind(svc, new FolderId(WellKnownFolderName.Calendar, request.CalendarId));
            FindItemsResults<Microsoft.Exchange.WebServices.Data.Appointment> appointments = calendarFolder.FindAppointments(new
             CalendarView(request.StartDate.Value, request.EndDate.Value));

            Calendar calendar = new Calendar();
            calendar.Appointments = new List<Thor.Domain.Appointment>();

            foreach (var appointment in appointments)
            {
                calendar.Appointments.Add(new Thor.Domain.Appointment
                {
                    Subject = appointment.Subject,
                    StartTime = appointment.Start,
                    EndTime = appointment.End,
                    Id = Guid.NewGuid()
                });
            }

            return calendar;
        }

        /// <summary>
        /// Books an appointment.
        /// </summary>
        /// <param name="appointmentRequest"></param>
        protected override AppointmentResponse OnBookDirect(AppointmentRequest appointmentRequest)
        {
            AppointmentResponse result = new AppointmentResponse();

            try
            {
                var svc = GetService(appointmentRequest.Credential, appointmentRequest.ServiceUrl);

                if (!appointmentRequest.IgnoreBusyData)
                {
                    if (this.IsBusy(appointmentRequest))
                    {
                        result.Success = false;
                        result.Message = string.Format("The calender for {0} is booked during the requested time.", appointmentRequest.CalendarId);
                        return result;
                    }
                }

                Microsoft.Exchange.WebServices.Data.Appointment appointment = new Microsoft.Exchange.WebServices.Data.Appointment(svc);

                appointment.Subject = appointmentRequest.Subject;
                appointment.Start = appointmentRequest.StartTime;
                appointment.End = appointmentRequest.EndTime;
                appointment.RequiredAttendees.Add(appointmentRequest.CalendarId);
                appointment.Save();

                result.Success = true;
                result.Message = "The appointment has been booked.";
            }
            catch (Exception ex)
            {
                result.Success = false;
                result.Message = ex.Message;
            }

            return result;
        }

        /// <summary>
        /// Books and appointment via a booking agent.
        /// </summary>
        /// <param name="appointmentRequest"></param>
        protected override AppointmentResponse OnBookViaAgent(AppointmentRequest appointmentRequest)
        {
            AppointmentResponse result = new AppointmentResponse();

            try
            {
                var svc = GetService(appointmentRequest.Credential, appointmentRequest.ServiceUrl);

                EmailMessage message = new EmailMessage(svc);
                message.Subject = "Meeting Request";

                message.Body = new MessageBody();
                message.Body.BodyType = BodyType.Text;
                message.Body.Text = this.GetBodyFromRequest(appointmentRequest);
                message.ToRecipients.Add(appointmentRequest.BookingAgentEmail);

                message.SendAndSaveCopy();

                result.Success = true;
                result.Message = string.Format("The appointment request for {0} has been sent to the booking agent ({1}).", appointmentRequest.CalendarId, appointmentRequest.BookingAgentEmail);
            }
            catch (Exception ex)
            {
                result.Success = false;
                result.Message = ex.Message;
            }

            return result;
        }

        protected abstract ExchangeService GetService(NetworkCredential credential, string serviceUrl);
    }
}
